/*
 * -------------------------------------- 
 * FileName: stack.c 
 * Content:
 *      sequential stack
 * Auther:  杨则瑞 
 * Encode:  utf-8 | CRLF                  
 * -------------------------------------- 
 */

#include<stack.h>

#define ns(title) stack_##title

#define try_error(errors) \
do { \
    if(self->_error_handler != NULL)\
    { \
        *self->_error_handler = errors; \
    } \
} while (0)



const int stack_default_length = 80;
const int stack_dft_incri = 80;

void stack_reserve(stack_t * self, size_t new_len)
{

    if(new_len <= stack_default_length)
    {
        return;
    }

    void * tmp = realloc(self->data,new_len * self->_elemSize);

    if(tmp == NULL) 
    {

        try_error(stack_INSUFFICIENT_MEMORORY);

        _debug("Insufficient Memory","Error");

        return;
    }

    self->data = tmp;
    self->_capacity = new_len;
}

void stack_init( stack_t * self, size_t elemSize, stack_error_t * error_handler)
{
    self->_error_handler = error_handler;
    self->_elemSize = elemSize;
    self->_len = 0;
    self->_capacity = stack_default_length;

    self->data = malloc(self->_elemSize * self->_capacity);

    if(self->data == NULL) 
    {
        self->_capacity = 0;

        try_error(stack_INSUFFICIENT_MEMORORY);

        _debug("Insufficient Memory","Error");

        return;
    }

    return;
}

void stack_uninit( stack_t * self)
{
    self->_elemSize = 0;
    self->_len = 0;
    self->_capacity = 0;

    free(self->data);

    return;
}


void * stack_top(stack_t * self)
{
    if(0 == self->_len)
    {
        try_error(stack_STACK_EMPTY);

        _debug("Empty Stack","Error");
        
        return NULL;
    }

    return self->data + (self->_len - 1) * (self->_elemSize);
}




void * stack_push_get(stack_t * self)
{
    
    if(self->_len + 1 > self->_capacity) 
    {
        size_t tmp = self->_capacity;
        stack_reserve(self, self->_capacity + stack_dft_incri);
        if(self->_capacity == tmp)
        {
            
            try_error(stack_INSUFFICIENT_MEMORORY);

            _debug("Insufficient Memory","Error");

            return NULL;
        }

    }

    self->_len += 1;

    return self->data + (self->_len - 1) * (self->_elemSize);
}


void stack_pop(stack_t * self)
{
    if(self->_len <= 0)
    {
        try_error(stack_STACK_EMPTY);

        _debug("Empty Stack","Error");

    }

    if((self->_len - 1)/stack_dft_incri != (self->_len - 1)/stack_dft_incri) 
    {
        size_t tmp = self->_capacity;
        stack_reserve(self, self->_capacity - stack_dft_incri);
        if(self->_capacity == tmp)
        {
            
            try_error(stack_INSUFFICIENT_MEMORORY);

            _debug("Insufficient Memory","Error");
            
            return;
        }
    }

    self->_len -= 1;

    return;
}

#undef try_error
#undef ns 
